package com.google.code.p.narcissus.core.model;

import java.awt.Rectangle;
import java.util.Date;
import java.util.List;

/**
 * <p>
 * Idea is to have this object (de)serialized through the use of JaxB/XStreams in order to persist the result easily as well as providing it to other applications (Narcissus-UI for
 * example)
 * </p>
 * <p>
 * A ComparisonResult should be immutable. this class makes sure of this.
 * </p>
 * <p>
 * Note that there are 3 <code>PictureComparisonResult</code>s possible in the enum, even if only 2 are used in this class. The third one ( <code>noReferenceFound</code>) is to be
 * used outside.
 * </p>
 * 
 * @author Jérôme JADOULLE [JADOJ] - 2 août 2010
 */
public class ComparisonResult {

	public enum PictureComparisonResult {
		noReferenceFound, verificationFail, verificationSuccess
	}

	private Date comparisonDate;

	private List<Rectangle> differenceZones;

	/**
	 * False positives are zones for which a difference has been reported but the difference is insignificant.
	 * <p>
	 * A very good example of false positives is the blinking caret in text zones.
	 * </p>
	 */
	private List<Rectangle> falsePositiveZones;

	/**
	 * The threshold that was used to define the error rectangles
	 */
	private int threshold;

	private PictureComparisonResult result;

	public ComparisonResult(List<Rectangle> differenceZones, List<Rectangle> falsePositiveZones, int threshold) {
		this(differenceZones, falsePositiveZones, threshold, null);
	}

	public ComparisonResult(List<Rectangle> differenceZones, List<Rectangle> falsePositiveZones, int threshold, PictureComparisonResult result) {
		super();
		this.differenceZones = differenceZones;
		this.falsePositiveZones = falsePositiveZones;
		this.threshold = threshold;
		if (result == null) {
			this.result = (this.differenceZones != null && this.differenceZones.size() > 0) ? PictureComparisonResult.verificationFail : PictureComparisonResult.verificationSuccess;
		} else {
			this.result = result;
		}
		comparisonDate = new Date();
	}

	public Date getComparisonDate() {
		return comparisonDate;
	}

	public List<Rectangle> getDifferenceZones() {
		return differenceZones;
	}

	public List<Rectangle> getFalsePositiveZones() {
		return falsePositiveZones;
	}

	public PictureComparisonResult getResult() {
		return result;
	}

	public int getThreshold() {
		return threshold;
	}

	/**
	 * Tells whether the pixel whose coordinates are passed in parameter is in an exclusion zone or not.
	 * 
	 * @param x
	 * @param y
	 * @return true if the pixel is in an exclusion zone, false if not.
	 */
	public boolean isAFalsePositivePixel(int x, int y) {
		return isInZones(x, y, falsePositiveZones);
	}

	/**
	 * <p>
	 * Note that a pixel that is in a difference zone is not necessarily different itself!
	 * </p>
	 * 
	 * @param x
	 * @param y
	 * @return
	 */
	public boolean isADifferenceZonePixel(int x, int y) {
		return isInZones(x, y, differenceZones);
	}

	private boolean isInZones(int x, int y, List<Rectangle> zones) {
		for (Rectangle currentZone : zones) {
			if (x >= currentZone.x && x <= (currentZone.width + currentZone.x) && y >= currentZone.y && y <= (currentZone.height + currentZone.y)) {
				return true;
			}
		}

		return false;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((differenceZones == null) ? 0 : differenceZones.hashCode());
		result = prime * result + ((falsePositiveZones == null) ? 0 : falsePositiveZones.hashCode());
		result = prime * result + ((this.result == null) ? 0 : this.result.hashCode());
		result = prime * result + threshold;
		return result;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null) {
			return false;
		}
		if (getClass() != obj.getClass()) {
			return false;
		}
		ComparisonResult other = (ComparisonResult) obj;
		if (differenceZones == null) {
			if (other.differenceZones != null) {
				return false;
			}
		} else if (!differenceZones.equals(other.differenceZones)) {
			return false;
		}
		if (falsePositiveZones == null) {
			if (other.falsePositiveZones != null) {
				return false;
			}
		} else if (!falsePositiveZones.equals(other.falsePositiveZones)) {
			return false;
		}
		if (result != other.result) {
			return false;
		}
		if (threshold != other.threshold) {
			return false;
		}
		return true;
	}

}